package org.aphelie.torg.ruler.internal.core.character;

import java.io.InputStream;

import javax.xml.parsers.SAXParser;

import org.aphelie.torg.ruler.core.IModelChangedEvent;
import org.aphelie.torg.ruler.core.ModelChangedEvent;
import org.aphelie.torg.ruler.core.character.ICharacterAttribute;
import org.aphelie.torg.ruler.core.character.ICharacterBase;
import org.aphelie.torg.ruler.core.character.ICharacterElement;
import org.aphelie.torg.ruler.core.character.ICharacterExtension;
import org.aphelie.torg.ruler.core.character.ICharacterLibrary;
import org.aphelie.torg.ruler.core.character.ICharacterModelBase;
import org.aphelie.torg.ruler.core.character.ICharacterModelFactory;
import org.aphelie.torg.ruler.core.character.ICharacterObject;
import org.aphelie.torg.ruler.core.character.ICharacterSketch;
import org.aphelie.torg.ruler.core.character.IExtensions;
import org.aphelie.torg.ruler.core.character.IExtensionsModelFactory;
import org.aphelie.torg.ruler.internal.core.AbstractNLModel;
import org.aphelie.torg.ruler.internal.core.TGRCore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.osgi.service.resolver.BundleDescription;


public abstract class AbstractCharacterModelBase extends AbstractNLModel implements ICharacterModelBase, ICharacterModelFactory {

    private static final long serialVersionUID = 1L;
    protected ICharacterBase fCharacterBase;
    private boolean enabled;
    private BundleDescription fBundleDescription;
    protected boolean fAbbreviated;

    public AbstractCharacterModelBase() {
        super();
    }

    public abstract String getInstallLocation();

    public abstract ICharacterBase createCharacterBase();

    public IExtensions createExtensions() {
        return createCharacterBase();
    }

    public IExtensionsModelFactory getFactory() {
        return this;
    }

    public ICharacterModelFactory getCharacterFactory() {
        return this;
    }

    public ICharacterBase getCharacterBase() {
        return getCharacterBase(true);
    }

    public ICharacterBase getCharacterBase(boolean createIfMissing) {
        if (fCharacterBase == null && createIfMissing) {
            fCharacterBase = createCharacterBase();
            setLoaded(true);
        }
        return fCharacterBase;
    }

    public void load(InputStream stream, boolean outOfSync) throws CoreException {
        load(stream, outOfSync, new CharacterHandler(fAbbreviated));
    }

    public void load(InputStream stream, boolean outOfSync, CharacterHandler handler) {
        if (fCharacterBase == null)
            fCharacterBase = createCharacterBase();

        ((CharacterBase) fCharacterBase).reset();
        setLoaded(false);
        try {
            SAXParser parser = getSaxParser();
            parser.parse(stream, handler);
            ((CharacterBase) fCharacterBase).load(handler.getDocumentElement(), handler.getSchemaVersion());
            setLoaded(true);
            if (!outOfSync)
                updateTimeStamp();
        } catch (Exception e) {
            TGRCore.log(e);
        }
    }

    /* TODO
    public void load(BundleDescription description, TGRState state) {
        setBundleDescription(description);
        ICharacterBase base = getCharacterBase();
        if (base instanceof Character)
            ((Character) base).load(description, state);
        else
            ((Fragment) base).load(description, state);
        updateTimeStamp();
        setLoaded(true);
    }
    */

    public IExtensions getExtensions() {
        return getCharacterBase();
    }

    public IExtensions getExtensions(boolean createIfMissing) {
        return getCharacterBase(createIfMissing);
    }

    public boolean isEnabled() {
        return enabled;
    }

    public boolean isFragmentModel() {
        return false;
    }

    public void reload(InputStream stream, boolean outOfSync) throws CoreException {
        load(stream, outOfSync);
        fireModelChanged(new ModelChangedEvent(this, IModelChangedEvent.WORLD_CHANGED, new Object[] {fCharacterBase}, null));
    }

    public void setEnabled(boolean newEnabled) {
        enabled = newEnabled;
    }

    public String toString() {
        ICharacterBase pluginBase = getCharacterBase();
        if (pluginBase != null)
            return pluginBase.getId();
        return super.toString();
    }

    protected abstract void updateTimeStamp();

    public ICharacterAttribute createAttribute(ICharacterElement element) {
        CharacterAttribute attribute = new CharacterAttribute();
        attribute.setModel(this);
        attribute.setParent(element);
        return attribute;
    }

    public ICharacterElement createElement(ICharacterObject parent) {
        CharacterElement element = new CharacterElement();
        element.setModel(this);
        element.setParent(parent);
        return element;
    }

    public ICharacterExtension createExtension() {
        CharacterExtension extension = new CharacterExtension();
        extension.setParent(getCharacterBase());
        extension.setModel(this);
        return extension;
    }

    /* TODO
    public ICharacterExtensionPoint createExtensionPoint() {
        CharacterExtensionPoint extensionPoint = new CharacterExtensionPoint();
        extensionPoint.setModel(this);
        extensionPoint.setParent(getCharacterBase());
        return extensionPoint;
    }
    */

    /* TODO
    public ICharacterImport createImport() {
        CharacterImport iimport = new CharacterImport();
        iimport.setModel(this);
        iimport.setParent(getCharacterBase());
        return iimport;
    }

    public ICharacterImport createImport(String pluginId) {
        CharacterImport iimport = new CharacterImport(this, pluginId);
        iimport.setParent(getCharacterBase());
        return iimport;
    }
    */
    
    public ICharacterSketch createSketch() {
        CharacterSketch sketch = new CharacterSketch();
        sketch.setModel(this);
        sketch.setParent(getCharacterBase());
        return sketch;
    }

    public ICharacterLibrary createLibrary() {
        CharacterLibrary library = new CharacterLibrary();
        library.setModel(this);
        library.setParent(getCharacterBase());
        return library;
    }

    public boolean isValid() {
        if (!isLoaded())
            return false;
        if (fCharacterBase == null)
            return false;
        return fCharacterBase.isValid();
    }

    public boolean isBundleModel() {
        return false;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.IModel#dispose()
     */
    public void dispose() {
        fBundleDescription = null;
        super.dispose();
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterModelBase#getBundleDescription()
     */
    public BundleDescription getBundleDescription() {
        return fBundleDescription;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterModelBase#setBundleDescription(org.eclipse.osgi.service.resolver.BundleDescription)
     */
    public void setBundleDescription(BundleDescription description) {
        fBundleDescription = description;
    }

}
